2 * CyberHeart, AVR ATtiny10 cellular automation micro-engine
3 * Copyright © 2021 Dmitry Shalnoff [interplaymedium.org]
4 * Licensed under the Apache License, Version 2.0
7 #define F_CPU 8000000UL // define CPU speed. actual clock speed is set using CLKPSR prescaler (see main()
9 // ---------------------- main settings ------------------------
11 // #define UART 1 // UART on PB1, for debugging purposes. due the lack of memory, LED PWM and watchdog delay will be disabled
13 // #define RULE 22 // rule 22 look better, 30 gives shorter sequences and looks like giving longer 'tales' (test needed)
14 #define LEN 8 // perimeter of the universe (bytes) (maximum for attiny10)
15 #define LOW_STAT_THRS 6 // 6 // 13 // low threshold for silent 'messaging', to make LED more "talkative" :) Check statistics for your RULE and change it
17 // #define BATT_LOW_LIMIT 231 // 231 // ADC battery meseurement limit for 'charging' message, 255 - minimum
19 // -------------------------------------------------------------
24 #include <util/delay.h>
27 #include <avr/interrupt.h>
28 #include <avr/pgmspace.h>
30 #include <avr/sleep.h>
31 #include <util/atomic.h>
36 #define LED PB2 // BLUE
37 #define LED2 PB1 // RED
39 #define EXP_LEN 10 // EXP, LED PWM glowing, number of steps
41 uint8_t uni[ LEN ]; // 1D compact manifold universe :)
42 uint8_t mode = 0, mode_init=0;
46 #define MODE_BLUE_RED 2
49 uint8_t i = LEN-1, tmp, triade;
50 uint8_t cnt = 0, cntMax = 0, rule;
57 // ---------------------- some useful defines -------------------
62 #define output_low(port,pin) port &= ~(1<<pin)
63 #define output_high(port,pin) port |= (1<<pin)
64 #define set_input(portdir,pin) portdir &= ~(1<<pin)
65 #define set_output(portdir,pin) portdir |= (1<<pin)
67 #define invert(port,pin) port ^= (1<<pin)
69 #define bit(x) (1 << (x))
72 void dly_500ns( uint16_t cnt ){
85 // ---------------------- simple UART TX -----------------------
89 void put_char( uint8_t c ){
92 output_low( PORTB, LED2 );
93 for( uint8_t i = 10; i; i-- ){ // 10 bits
95 _delay_us( 1000000 / 9600 ); // bit duration / (8MHz, 9600, 104 us) Delay 832 cycles
97 if( c & 1 ) output_low( PORTB, LED2 ); else output_high( PORTB, LED2 ); // data bit 0 / data bit 1 or stop bit
102 void put_str_P(PGM_P str){
105 while (pgm_read_byte(s)) put_char( pgm_read_byte(s++) );
109 void put_str(char * str){
110 while (*str) put_char( *str++ );
113 void put_num( uint8_t num ){
117 for (div = 1; num / div >= 10 ; div *= 10);
120 put_char( 48 + (( num / div ) % 10 ));
128 // ---------------------- LED PWM ------------------------------
132 #define INT_PWM_DISABLE ({ TIMSK0 &= ~((1<<OCIE0A) | (1<<TOIE0)); }) // compare interrupt off
133 #define INT_PWM_ENABLE ({ TIMSK0 |= ((1<<OCIE0A) | (1<<TOIE0)); }) // compare interrupt on
135 ISR ( TIM0_COMPA_vect ){
136 if ( mode == MODE_BLUE || mode == MODE_BLUE_RED) output_low(PORTB, LED);
137 if ( mode == MODE_RED || mode == MODE_BLUE_RED) output_low(PORTB, LED2);
140 ISR ( TIM0_OVF_vect ){
141 if ( mode == MODE_BLUE || mode == MODE_BLUE_RED) output_high(PORTB, LED);
142 if ( mode == MODE_RED || mode == MODE_BLUE_RED) output_high(PORTB, LED2);
147 // Timer0 in mode 14, fast PWM with ICR0 as top.
148 // Enable OC0A and OC0B, lower mode bits
149 // TCCR0A = (1<<COM0A1) | (1<<COM0B1) | (1<<WGM01);
151 TIMSK0 = (1<<OCIE0A); // Eneable interrupt (since I'm uisng PB2, I can't use integrated pin :( )
152 // TIMSK0 = (1<<OCIE0A) | (1<<OCIE0B); // Eneable interrupt (since I'm uisng PB2, I can't use integrated pin :( ) + second interrupt for beeper
153 ICR0 = 1000; // Set top to 1000
154 TCCR0B = (1<<CS01) | (1<<WGM03) | (1<<WGM02); // Start timer with prescaler 1:8 and set upper mode bits. WGM0 = 1110 Fast PWM ICR0 TOP TOP
159 // ---------------------- read ADC -----------------------------
164 set_input(DDRB, ADC); // input
165 output_low(PORTB, ADC); // no pull-up
167 PRR &= ~(1<<PRADC); // power on ADC
168 ADMUX = 0; // channel selection (PB0) MUX1 = 0, MUX0 = 0
169 // ADMUX = (1<<MUX0); // channel selection (PB1) MUX1 = 0, MUX0 = 1
173 dly_500ns((uint16_t)2000 * 30);
175 ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // enable ADC + set prescaler 128
176 ADCSRA |= (1<<ADSC); // start convertion (in single mode)
177 while ((ADCSRA & (1<<ADIF))==0); // waiting for conversion completes flag, see ADCL register for result
178 ADCSRA = (1<<ADIF); // clear flag
180 ADCSRA &= ~(1<<ADEN); // disable ADC (for sleep mode)
181 PRR |= (1<<PRADC); // power off ADC
183 set_output(DDRB, ADC); // output
184 output_low(PORTB, ADC); // no pull-up
187 // ---------------------- cell automation tools -----------------
189 void shift_left( uint8_t shift) {
197 uint8_t next = (uni[i] & 0x80) ? 1 : 0;
198 uni[i] = carry | (uni[i] << 1);
203 uni[ LEN - 1 ] |= carry ;
208 // ---------------------- for power down delay -----------------
210 EMPTY_INTERRUPT(WDT_vect); // clearing the flag (wake up by WDT interrupt)
212 // ====================== MAIN =================================
216 // Set CPU speed by setting clock prescaler
218 CCP = 0xD8; // signature to unlock protection of CLKPSR
219 CLKPSR = 0; // sets the clock division factor
221 // disable digital inputs (for ADC and so)
225 // IO and interrupts init
229 ledPWMInint(); // must be before DDR setup
233 // // not required since doing in readADC()
234 // set_input(DDRB, ADC); // input
235 // output_low(PORTB, ADC); // no pull-up
237 set_output(DDRB, LED);
238 set_output(DDRB, LED2);
239 set_output(DDRB, SND);
244 // put_str_P( PSTR("^__.__^\n") );
245 // put_str_P( PSTR("\n .:::. .:::.\n:::::::.:::::::\n:::::::::::::::\n':::::::::::::'\n ':::::::::'\n ':::::'\n ':'\n"));
248 // ----------- initial randomizer -----------------
254 uni[ i ] = pgm_read_byte( uni[ i+1 ] ) + ADCL;
255 mode_init ^= uni[ i ] % 4;
258 if ( mode_init == MODE_ROTATE ) rule = 22; else rule = 30; // rule 22 look better, 30 gives shorter sequences and looks like giving longer 'tales' (test needed)s
264 put_num( mode_init );
265 put_str( " RULE: " );
270 // ------------ cell automata -------------
276 for (i=0; i < LEN*8; i++){
278 // new cell calculation
280 if ( i > LEN * 8 - 4 ) triade = (tmp >> (LEN*8 - i) ); else triade = uni[ 0 ];
281 if ( rule & bit(7 & triade) ) uni[ 0 ] |= bit(2); else uni[ 0 ] &= ~bit(2);
283 put_char( (( uni[ 0 ] >> 2) & 1) ? '*' : ' ');
287 if ( mode_init != MODE_ROTATE ){
288 // long sequence check
290 // if ( (( uni[ 0 ] >> 2) & 1) == (( uni[ 0 ] >> 3) & 1) ) {
291 if ( (( uni[ 0 ] >> 2) & 0b11) == 0b00 ) {
297 if ( cnt > cntMax ) {
305 if ( cnt >= cntMax || cnt > LOW_STAT_THRS ) {
307 if ( cnt == cntMax && cnt > LOW_STAT_THRS ) mode = MODE_BLUE_RED;
309 // ------------- glow --------------
315 OCR0A *= 2; // fade in
317 dly_500ns( 65535 ); // 2000 * 30
322 if ( cnt > cntMax ) {
327 m = uni[ cnt % LEN ] % 5;
337 asm volatile ( //500ns
345 dly_500ns((uint16_t)2000 * 30);
353 OCR0A /= 2; // fade out
355 dly_500ns( 65535 ); // 2000 * 30
358 // ---------------------------------
368 } else { // MODE_ROTATE
370 if ( (( uni[ 0 ] >> 2) & 1) ) {
371 output_low(PORTB, LED);
372 output_high(PORTB, LED2);
374 output_high(PORTB, LED);
375 output_low(PORTB, LED2);
378 dly_500ns( 7300 ); // define speed of oscilation (2000 * x)
384 if ( mode_init == MODE_ROTATE ){
386 output_low(PORTB, LED);
387 output_low(PORTB, LED2);
389 j = 10200; // length of backflight beep
394 asm volatile ( // 50 us
404 // invert(PORTB, SND);
406 shift_left( LEN*8-1 ); // 1 bit right shift, not really required if you don't visualize it, just for beauty of canonical view
413 // ----------------------------------------
418 // put_str_P( PSTR(" DAC: ") );
421 // put_str_P( PSTR("\n") );
428 // ------------- delay --------------------
430 if ( mode_init != MODE_ROTATE ) { // disabled in Rotate Mode
433 wdt_enable( WDTO_8S ); // <-- delay
436 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
439 NONATOMIC_BLOCK( NONATOMIC_RESTORESTATE ) {
447 DIDR0 = 0b00001111; // disable digital inputs (for ADC)
449 // // not required since doing in readADC()
450 // set_input(DDRB, ADC); // input
451 // output_low(PORTB, ADC); // no pull-up
455 // ----------------------------------------